#![feature(const_generics)]
#![feature(const_evaluatable_checked)]
#[macro_use]
extern crate proc_macro;

use proc_macro::TokenStream;
use quote::quote;
use syn;

#[proc_macro_derive(Fifoer)]
pub fn derive_fifoer_fn(input: TokenStream) -> TokenStream {
    let ast = syn::parse(input).unwrap();
    impl_fifoer_macro(&ast)
}

fn impl_fifoer_macro(ast: &syn::DeriveInput) -> TokenStream {
    let name = &ast.ident;
    let generics = &ast.generics;
    let where_clause = &ast.generics.where_clause;
    let gen = quote! {
        impl#generics Fifoer for #name #generics #where_clause {
            fn local_get<P>(&mut self) -> Result<Option<MBox<P>>>{
                if self.addon.local_len()==0usize {
                    Ok(None)
                } else {
                    let o = self.addon.get_local_rp();
                    let raw = self.to_raw(o);
                    Ok(Some(MBox::<P>::attach(&raw,0usize)?))
                }
            }
            fn get<P>(&mut self) -> Result<Option<MBox<P>>>{
                if self.addon.globe_len()==0usize {
                    Ok(None)
                } else {
                    let o = self.addon.get_globe_rp();
                    let raw = self.to_raw(o);
                    Ok(Some(MBox::<P>::attach(&raw,0usize)?))
                }
            }
            fn push<P>(&mut self, p :&UBox<P>) {
                self.addon.push(p.offset())
            }
            fn globe_sync(&mut self) { self.addon.sync_globe_rp() } 
            fn local_reset(&mut self) { self.addon.local_reset() }
            fn local_len(&self) -> usize { self.addon.local_len() }
            fn globe_len(&self) -> usize { self.addon.globe_len() }
            fn get_fifo_pointer(&mut self) -> &mut FifoPointer { &mut self.addon }
        }
    };
    gen.into()
}

#[proc_macro_derive(FifoPointer)]
pub fn derive_fifopointer_fn(input: TokenStream) -> TokenStream {
    let ast = syn::parse(input).unwrap();
    impl_fifo_pointer_macro(&ast)
}

fn impl_fifo_pointer_macro(ast: &syn::DeriveInput) -> TokenStream {
    let name = &ast.ident;
    let generics = &ast.generics;
    let where_clause = &ast.generics.where_clause;
    let gen = quote! {
        impl#generics FifoPointer for #name #generics #where_clause {
            fn local_reset(&mut self) { self.local_rp = self.fifo.point.sp;}
            fn sync_globe_rp(&mut self) {
                self.fifo.point.set_rp(self.local_rp);
            }
            fn local_len(&self) -> usize { (L::BUF_LEN+self.fifo.point.sp-self.local_rp)%L::BUF_LEN}
            fn globe_len(&self) -> usize { (L::BUF_LEN+self.fifo.point.sp%L::BUF_LEN-self.fifo.point.rp%L::BUF_LEN)%L::BUF_LEN}
            fn push(&mut self,off : usize) {
                if self.globe_len()==L::BUF_LEN-1 {
                    self.fifo.point.atomic_inc_rp();
                }
                let sp = self.fifo.point.atomic_inc_sp();
                *self.fifo.buf.uaindex_mut(sp) = off;
            }            
            fn get_globe_rp(&mut self) -> usize {
                let rp = self.fifo.point.atomic_inc_rp();
                *self.fifo.buf.uaindex(rp)
            }
            fn get_local_rp(&mut self) -> usize {
                *self.fifo.buf.uaindex(inc::<L>(&mut self.local_rp))
            } 
        }
    };
    gen.into()
}

